package com.novoda.runbuddy.util;

import java.util.Iterator;
import java.util.Map;

import android.location.Location;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.novoda.runbuddy.model.Run;

public class LocationUtils {

	/***************************************************************************
	 * The MapBoundsChecker is used to determine whether a location is contained
	 * within the viewable bounds of a MapView.
	 */
	public static class MapBoundsChecker {
		private int	minLatitude;
		private int	maxLatitude;
		private int	minLongitude;
		private int	maxLongitude;

		public MapBoundsChecker(MapView mapView) {
			GeoPoint mapCenter = mapView.getMapCenter();
			minLatitude = mapCenter.getLatitudeE6() - (mapView.getLatitudeSpan() / 2);
			maxLatitude = mapCenter.getLatitudeE6() + (mapView.getLatitudeSpan() / 2);
			minLongitude = mapCenter.getLongitudeE6() - (mapView.getLongitudeSpan() / 2);
			maxLongitude = mapCenter.getLongitudeE6() + (mapView.getLongitudeSpan() / 2);
		}

		/***********************************************************************
		 * isLocationOnMap() returns true if specified location is within the
		 * viewable bounds of the MapView specified during instantiation of the
		 * MapBoundsChecker.
		 * 
		 * Note: location must be in microdegrees format
		 */
		public boolean isLocationOnMap(Location loc) {
			boolean onMap = true;

			int latitude = (int) (loc.getLatitude());
			int longitude = (int) (loc.getLongitude());

			if ((latitude < minLatitude) || (latitude > maxLatitude) || (longitude < minLongitude) || (longitude > maxLongitude)) {
				onMap = false;
			}

			return onMap;
		}

	}

	/***************************************************************************
	 * getJogLocationFromDistance() This function determines the location of a
	 * point which is a certain distance into a jog. It will interpolate between
	 * the routePoints of the jog to determine the location.
	 * 
	 * @param jog -
	 *            the jog containingthe route to be followed
	 * @param distance -
	 *            the distance (in meters) into the jog to find location of
	 * @return location
	 */
	public static Location getJogLocationFromDistance(Run jog, double distance) {
		Location currentLoc = null;

		double routeDistance = jog.getDistance();

		// This is intended to work with laps. May behave badly if used
		// incorrectly.
		while (distance > routeDistance) {
			distance -= routeDistance;
		}

		Location loc = null;
		double distanceSoFar = 0.0;

		Iterator<Map.Entry<Long, Location>> it = jog.getlocationsAndTimes().entrySet().iterator();
		while (it.hasNext()) {
			Location nextLoc = it.next().getValue();
			if (loc != null) {
				if ((distance >= distanceSoFar) && (distanceSoFar + loc.distanceTo(nextLoc) > distance)) {
					// interpolate the position
					currentLoc = interpolateBetweenLocations(loc, nextLoc, distance - distanceSoFar);

					break;
				}
				distanceSoFar += loc.distanceTo(nextLoc);
			}

			loc = nextLoc;
		}

		return currentLoc;
	}

	/***************************************************************************
	 * interpolateBetweenLocations() This function determines the location of a
	 * point that is between two locations. The location returned lies on the
	 * line running through location1 and location2, placed with distance from
	 * location1, in the direction of location2.
	 * 
	 * @param location1 -
	 *            first location
	 * @param location2 -
	 *            second location
	 * @param distance -
	 *            distance from location1, in meters
	 * @return interpolated location
	 */
	public static Location interpolateBetweenLocations(Location location1, Location location2, double distance) {
		Location interLoc = null;

		interLoc = new Location("location");

		// interpolate the position
		double ratio = distance / location1.distanceTo(location2);
		interLoc.setLatitude(location1.getLatitude() + ratio * (location2.getLatitude() - location1.getLatitude()));
		interLoc.setLongitude(location1.getLongitude() + ratio * (location2.getLongitude() - location1.getLongitude()));
		interLoc.setTime((long) (location1.getTime() + ratio * (location2.getTime() - location1.getTime())));

		return interLoc;
	}
}
